package com.example.fireassistant_server.service.impl;

import com.example.fireassistant_server.dto.PointDto;
import com.example.fireassistant_server.dto.PointSearchFilter;
import com.example.fireassistant_server.dto.polyline.PolyLineCreateRequest;
import com.example.fireassistant_server.dto.polyline.PolyLineDto;
import com.example.fireassistant_server.dto.polyline.PolyLineUpdateRequest;
import com.example.fireassistant_server.entity.*;
import com.example.fireassistant_server.exception.BizException;
import com.example.fireassistant_server.exception.ExceptionType;
import com.example.fireassistant_server.mapper.PointMapper;
import com.example.fireassistant_server.mapper.PolyLineMapper;
import com.example.fireassistant_server.repository.LinePointRepository;
import com.example.fireassistant_server.repository.PointRepository;
import com.example.fireassistant_server.repository.PolyLineRepository;
import com.example.fireassistant_server.repository.spec.LineSpecification;
import com.example.fireassistant_server.repository.spec.PointSearchCriteria;
import com.example.fireassistant_server.repository.spec.SearchOperation;
import com.example.fireassistant_server.service.FileService;
import com.example.fireassistant_server.service.PolyLineService;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import javax.persistence.EntityManager;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

@Service
public class PolyLineServiceImpl implements PolyLineService {

    PolyLineRepository polyLineRepository;

    PolyLineMapper polyLineMapper;

    PointRepository pointRepository;

    PointMapper pointMapper;

    private EntityManager entityManager;

    LinePointRepository linePointRepository;

    private FileService fileService;

    // 添加线段
    @Override
    public PolyLineDto addLine(PolyLineCreateRequest polyLineCreateRequest, User user) {
        PolyLine polyLine = new PolyLine();
        //标记点
        List<PointDto> points = polyLineCreateRequest.getPoints();
        BigDecimal sumLatitude = new BigDecimal(0);
        BigDecimal sumLongitude= new BigDecimal(0);
        for (PointDto pointDto : points) {
            sumLatitude = sumLatitude.add(pointDto.getLatitude());
            sumLongitude = sumLongitude.add(pointDto.getLongitude());
        }
        sumLatitude = sumLatitude.divide(BigDecimal.valueOf(points.size()), 12, RoundingMode.HALF_UP);
        sumLongitude = sumLongitude.divide(BigDecimal.valueOf(points.size()), 12, RoundingMode.HALF_UP);
        polyLine.setLatitude(sumLatitude);
        polyLine.setLongitude(sumLongitude);

        polyLine.setDescription(polyLineCreateRequest.getDescription());
        polyLine.setBackground_color(polyLineCreateRequest.getBackground_color());
        polyLine.setUserId(user.getId());
        //线段
        PolyLineDto polyLineDto=polyLineMapper.toDto(polyLineRepository.save(polyLine));
        //存储中间表
        points.forEach(item -> {
            //改造item使之成为point,并存储,存储时才会生成id
            Point point = pointMapper.createEntity(item);
            pointRepository.save(point);
            LinePoint linePoint = new LinePoint();
            linePoint.setPointId(point.getId());
            linePoint.setLineId(polyLineDto.getId());
            linePointRepository.save(linePoint);
        });
        entityManager.unwrap(Session.class).clear();
        return get(polyLineDto.getId());
    }


    // 根据id 获取线段
    @Override
    public PolyLineDto get(String id){
        Optional<PolyLine> polyLine = polyLineRepository.findById(id);
        if (!polyLine.isPresent()) {
            throw new BizException(ExceptionType.POLYlINE_NOT_FOUND);
        }
        return polyLineMapper.toDto(polyLine.get());
    }

    @Override
    @Transactional
    public void delete(String id) {
        Optional<PolyLine> polyLine = polyLineRepository.findById(id);
        if (!polyLine.isPresent()) {
            throw new BizException(ExceptionType.POLYlINE_NOT_FOUND);
        }

        // 谁的外键指向另外一个，先删除谁
        polyLineRepository.deleteById(id);

        // 先删除对应的file
        List<File> files=polyLine.get().getFiles();

        if (!files.isEmpty()){
            files.forEach(item->{
                fileService.delete("polyLine",item.getId());
            });
        }

        //删除对应的点
        List<Point> points = polyLine.get().getPoints();
        if (!points.isEmpty()){
            points.forEach(item->{
                linePointRepository.deleteLinePointByPointId(item.getId());
                pointRepository.deleteById(item.getId());
            });
        }

    }

    @Override
    public Page<PolyLineDto> search(PointSearchFilter pointSearchFilter) {

        LineSpecification specs = new LineSpecification();
        //添加所查询的latitude范围
        if (!Objects.equals(pointSearchFilter.getMinLatitude(), null) && !Objects.equals(pointSearchFilter.getMaxLatitude(), null)) {
            specs.add(new PointSearchCriteria("latitude", pointSearchFilter.getMinLatitude(), pointSearchFilter.getMaxLatitude(), SearchOperation.BETWEEN));
        }
        //添加所查询的longitude范围
        if (!Objects.equals(pointSearchFilter.getMinLongitude(), null) && !Objects.equals(pointSearchFilter.getMaxLongitude(), null)){
            specs.add(new PointSearchCriteria("longitude", pointSearchFilter.getMinLongitude(), pointSearchFilter.getMaxLongitude(), SearchOperation.BETWEEN));
        }
        return polyLineRepository.findAll(specs, pointSearchFilter.toPageable()).map(polyLineMapper::toDto);
    }

    @Override
    @Transactional
    public PolyLineDto update(String id, PolyLineUpdateRequest polyLineUpdateRequest) {
        Optional<PolyLine> polyLine = polyLineRepository.findById(id);
        if (!polyLine.isPresent()) {
            throw new BizException(ExceptionType.POLYlINE_NOT_FOUND);
        }
        //原来对应的点
        List<Point> points = polyLine.get().getPoints();

        //新请求的点
        List<PointDto> newPoints = polyLineUpdateRequest.getPoints();

        if(polyLineUpdateRequest.getDescription()!= null){
            //更新polyline基本信息
            polyLine.get().setDescription(polyLineUpdateRequest.getDescription());
        }
        BigDecimal sumLatitude = new BigDecimal(0);
        BigDecimal sumLongitude= new BigDecimal(0);
        for (PointDto pointDto : newPoints) {
            sumLatitude = sumLatitude.add(pointDto.getLatitude());
            sumLongitude = sumLongitude.add(pointDto.getLongitude());
        }
        sumLatitude = sumLatitude.divide(BigDecimal.valueOf(newPoints.size()), 12, RoundingMode.HALF_UP);
        sumLongitude = sumLongitude.divide(BigDecimal.valueOf(newPoints.size()), 12, RoundingMode.HALF_UP);
        polyLine.get().setLatitude(sumLatitude);
        polyLine.get().setLongitude(sumLongitude);


//         为line添加没有的点
        if (!newPoints.isEmpty()){
            newPoints.forEach(newPoint->{
                AtomicInteger count = new AtomicInteger(0);
                points.forEach(item->{
                    if (newPoint.getLatitude().compareTo(item.getLatitude())== 0 && newPoint.getLongitude().compareTo(item.getLongitude())==0){
                        count.getAndIncrement();
                    }
                });

                // 在points这种没有与之相同的点 需要添加
                if (count.get()==0){
                    // points中 没有 newPoint 添加newPoint
                    //改造item使之成为point,并存储,存储时才会生成id
                    Point point = pointMapper.createEntity(newPoint);
                    pointRepository.save(point);
                    LinePoint linePoint = new LinePoint();
                    linePoint.setPointId(point.getId());
                    linePoint.setLineId(id);
                    linePointRepository.save(linePoint);
                }
            });
        }

         //删除之前的点
        if (!points.isEmpty()){
            points.forEach(item -> {
                AtomicInteger count = new AtomicInteger(0);
                newPoints.forEach(newPoint->{
                    if (newPoint.getLatitude().compareTo(item.getLatitude())== 0 && newPoint.getLongitude().compareTo(item.getLongitude())==0){
                        count.getAndIncrement();
                    }
                });
                // 在newPoints这种没有与之相同的点 需要删除
                if (count.get()==0){
                    linePointRepository.deleteLinePointByPointId(item.getId());
                    pointRepository.deleteById(item.getId());
                }
            });
        }

        // 解决缓存问题
        pointRepository.flush();
        entityManager.unwrap(Session.class).clear();
        return get(id);
    }

    @Autowired
    public void setPolyLineRepository(PolyLineRepository polyLineRepository) {
        this.polyLineRepository = polyLineRepository;
    }

    @Autowired
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Autowired
    public void setPolyLineMapper(PolyLineMapper polyLineMapper) {
        this.polyLineMapper = polyLineMapper;
    }

    @Autowired
    public void setPointRepository(PointRepository pointRepository) {
        this.pointRepository = pointRepository;
    }

    @Autowired
    public void setLinePointRepository(LinePointRepository linePointRepository) {
        this.linePointRepository = linePointRepository;
    }

    @Autowired
    public void setPointMapper(PointMapper pointMapper) {
        this.pointMapper = pointMapper;
    }

    @Autowired
    public void setFileService(FileService fileService) {
        this.fileService = fileService;
    }
}
